/*
 * The Smacker library was ported from C# for use in Silvie. The original
 * library is copyright Chris Toshok. It is released under an X11 license.
 */

package utils.smacker;

import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * A utility class for reading groups of bits from a stream
 */
public class SmackerBitStream {
	/*
	 * Raising this value causes more bytes to be cached in the stream and
	 * reduces the number of accesses to disk
	 */
	private static final int MAX_BYTES = 512;
	private RandomAccessFile mStream;
	private int mCurrentByte;
	private int mCurrentBit;
	private int nbBytes;
	private byte[] bytes = new byte[MAX_BYTES];

	public SmackerBitStream(RandomAccessFile mStream) {
		this.mStream = mStream;
	}

	public RandomAccessFile getmStream() {
		return this.mStream;
	}

	/*
	 * This method needs to be lightning fast: it's run thousends of time when
	 * decoding an SMK Frame.
	 */
	public int readBits(int BitCount) throws IOException {
		if (BitCount > 16) {
			throw new IllegalArgumentException("Maximum BitCount is 16");
		}
		// We need BitCount bits
		int result = 0;
		int bitsRead = 0;
		while (BitCount > 0) {
			if (this.mCurrentByte >= this.nbBytes) {
				this.nbBytes = this.mStream.read(this.bytes, 0, MAX_BYTES);
				if (this.nbBytes == -1) {
					throw new EOFException();
				}
				this.mCurrentByte = 0;
				this.mCurrentBit = 0;
			}
			if (this.mCurrentBit + BitCount < 8) {
				// Everything fits in this byte
				result |= ((this.bytes[this.mCurrentByte] >> this.mCurrentBit) & (0xffff >> (16 - BitCount))) << bitsRead;
				this.mCurrentBit = BitCount + this.mCurrentBit;
				BitCount = 0;
			} else {
				// Read all bits left in this byte
				int bitsToRead = 8 - this.mCurrentBit;
				result |= ((this.bytes[this.mCurrentByte] >> this.mCurrentBit) & (0xffff >> (16 - bitsToRead))) << bitsRead;
				bitsRead += bitsToRead;
				this.mCurrentByte++;
				this.mCurrentBit = 0;
				BitCount -= bitsToRead;
			}
		}
		return result;
	}

	public void reset() throws IOException {
		this.mStream.seek(0);
		this.mCurrentByte = 0;
		this.mCurrentBit = 0;
	}
}
